#include <vector>
#include <stdlib.h>
#include <time.h>
#include <math.h>
const int param6_pop = 3;
const int param6_iters = 1;
const int param6_randf = 2;
const int param6_randc = 2;
const int param6_cross = 2;
const int param6_randcase = 0;
const int param6_inversions = 3;
const int param6_mutations = 1;
const int param6_mutations_count[] = {2};
const int param6_mutations_bitlen[] = {3};
const int param6_inversions_count[] = {2, 2, 2};
const int param6_inversions_bitlen[] = {1, 2, 3};

std::vector<unsigned> funcs6; //6-var functions' population
std::vector<int> weights6; //6-var functions' weights

#include "../NLLibrary.h"

typedef LibraryInfo<5> Lib5;
NLLibrary<Lib5> nllib;

inline int getrandom(int number, int pop) {//getting random number from (0,pop-1) with respect to chosen case (number)
    int r = rand();
    switch (number) {
        case 0: return r % pop;
            break;
        case 1: return ((unsigned) (log(.0 + r) / log(.0 + RAND_MAX) * pop)) % pop;
            break;
        case 2: return ((unsigned) ((double) r * (double) r / (double) RAND_MAX / (double) RAND_MAX * (double) pop)) % pop;
            break;
        case 3: return pop - ((unsigned) (sqrt(.0 + r) / sqrt(.0 + RAND_MAX) * pop)) % pop;
            break;
    }
    return r % pop;
}

inline unsigned absmin5(unsigned fr) {
    unsigned s;
    nllib.get((UInt64&)fr, (UInt8&)s);
    return s;
}


inline int add_func6(const unsigned fexp0, const unsigned frem0, const unsigned frem1) {//add new function (fexp0) in population if it's not there
    int w = absmin5(fexp0) + absmin5(fexp0^frem0) + absmin5(fexp0^frem1);
    unsigned i, j;
    for (j = 0; (j < weights6.size()) && (w > weights6[j]); j++);
    for (i = j; (i < weights6.size()) && (w == weights6[i]); i++) if (funcs6[i] == fexp0) return 0;
    if (j < weights6.size()) {
        weights6.insert(weights6.begin() + j, w);
        if (weights6.size() > param6_pop) weights6.erase(weights6.end() - 1);
        funcs6.insert(funcs6.begin() + j, fexp0);
        if (funcs6.size() > param6_pop) funcs6.erase(funcs6.end() - 1);
        return 1;
    }
    return 0;
}

int genmin6(const unsigned rem0, const unsigned rem1, unsigned &pop) {//6-variable function (rem0,rem1) minimization starting with function 'pop'
    unsigned i, j, k, //iterators
            f, //current function
            w, //current weight
            n1, n2, n3, //random numbers
            fl; //flag

    funcs6.resize(0);
    weights6.resize(0);
    //generating population
    funcs6.push_back(pop);
    weights6.push_back(absmin5(funcs6[0]) + absmin5(funcs6[0]^rem0) + absmin5(funcs6[0]^rem1));
    for (i = 1; i < param6_pop; i++) {
        if (i < 4)
            switch (i) {
                case 1:f = rem0;
                    break;
                case 2:f = rem1;
                    break;
                case 3:f = rem0^rem1;
                    break;
            } else f = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
        w = absmin5(f) + absmin5(f^rem0) + absmin5(f^rem1);
        for (j = 0; (j < i) && (w > weights6[j]); j++);
        fl = 0;
        {
            weights6.insert(weights6.begin() + j, w);
            funcs6.insert(funcs6.begin() + j, f);
        }
    }

    for (j = 0; j < param6_iters; j++) {//processing iterations
        for (i = 0; i < param6_randf; i++)//random functions
        {
            f = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
            w = add_func6(f, rem0, rem1);
        }
        for (i = 0; i < param6_randc; i++)//random crossovers
        {
            n1 = getrandom(param6_randcase, funcs6.size());
            n2 = getrandom(param6_randcase, funcs6.size());
            n3 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
            f = (funcs6[n1] & n3)^(funcs6[n2]&(~n3));
            w = add_func6(f, rem0, rem1);
            f = (funcs6[n2] & n3)^(funcs6[n1]&(~n3));
            w = add_func6(f, rem0, rem1);
        }
        for (i = 0; i < param6_cross; i++)//half-to-half crossovers
        {
            n1 = getrandom(param6_randcase, funcs6.size());
            n2 = getrandom(param6_randcase, funcs6.size());
            f = (funcs6[n1]&((unsigned) ((1 << 16) - 1) << 16))^(funcs6[n2] % (1 << 16));
            w = add_func6(f, rem0, rem1);
            f = (funcs6[n2]&((unsigned) ((1 << 16) - 1) << 16))^(funcs6[n1] % (1 << 16));
            w = add_func6(f, rem0, rem1);
        }
        for (k = 0; k < param6_mutations; k++)//mutations
            for (i = 0; i < param6_mutations_count[k]; i++) {
                n1 = getrandom(param6_randcase, funcs6.size());
                f = funcs6[n1];
                if (param6_mutations_bitlen[k] != 32) {
                    n2 = ((unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand()) % ((unsigned) (1 << param6_mutations_bitlen[k]));
                    n3 = rand() % (32 - param6_mutations_bitlen[k]);
                } else {
                    n2 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
                    n3 = 0;
                }
                f = funcs6[n1]^((unsigned) n2 << n3);
                w = add_func6(f, rem0, rem1);
            }
        for (k = 0; k < param6_inversions; k++)//inversions
            for (i = 0; i < param6_inversions_count[k]; i++) {
                n1 = getrandom(param6_randcase, funcs6.size());
                f = funcs6[n1];
                if (param6_inversions_bitlen[k] != 32) {
                    n2 = (unsigned) (1 << param6_inversions_bitlen[k]) - 1;
                    n3 = rand() % (32 - param6_inversions_bitlen[k]);
                } else {
                    n2 = (unsigned) (1 << 31) - 1 + (unsigned) (1 << 31);
                    n3 = 0;
                }
                f = funcs6[n1]^((unsigned) n2 << n3);
                w = add_func6(f, rem0, rem1);
            }

    }
    pop = funcs6[0];
    return weights6[0];
}

inline unsigned min6(const unsigned frem0, const unsigned frem1, unsigned &pop) {//minimize 6-variable function
    //if (useabsmin) return absmin6(rem60,rem61);
    unsigned m = genmin6(frem0, frem1, pop), m1;
    m1 = genmin6(frem0, frem1, pop);
    if (m1 < m) m = m1;
    m1 = genmin6(frem0, frem1, pop);
    if (m1 < m) m = m1;
    return m;
}
